<?php
/**
 * Zend_Db_Table_Abstract based class implementing the
 * <a href="http://www.martinfowler.com/eaaCatalog/tableDataGateway.html">Table Data Gateway pattern</a>
 * to access tables
 *
 * It uses Application::code to guess what is the prefix of the table in database
 * It uncamelizes the class name to guess
 * If one of both previous features do not suit your needs, hardcode the name of the table in $this_name
 * <code>
 * protected $_name = 'my_table';
 * </code>
 *
 * @see Application
 */
abstract class BaseTable extends Zend_Db_Table_Abstract
{
    /**
     * Constructor
     *
     * It uses Application::code to guess what is the prefix of the table in database
     * and uncamelizes the class name to guess remaining the table name.
     * If you do not want to rely on that feature, you can hardcode the name
     * of the table.
     * <code>
     * protected $_name = 'my_table';
     * </code>
     *
     * It also tries to remove the Table suffix to guess the name of the Row class, e.g. UserTable => User
     * If you do not want to rely on that feature, you can hardcode the name
     * of the table.
     * <code>
     * protected $_rowClass = 'MyUser';
     * </code>     *
     * For more informations about what you can
     * Supported params for $config are:
     * - db              = user-supplied instance of database connector,
     *                     or key name of registry instance.
     * - name            = table name.
     * - primary         = string or array of primary key(s).
     * - rowClass        = row class name.
     * - rowsetClass     = rowset class name.
     * - referenceMap    = array structure to declare relationship
     *                     to parent tables.
     * - dependentTables = array of child tables.
     * - metadataCache   = cache for information from adapter describeTable().
     *
     * @param array $config Array of user-specified config options, or just the Db Adapter.
     * @return void
     */
    function __construct(array $config = array())
    {
        /* Get a camelcase word and try to guess a table name if no _name is given */
        if (!$this->_name && !isset($config['name'])) {
            $this->_name = strtolower(preg_replace('/(?<=\\w)([A-Z])/',
                                                   '_\\1',
                                                   substr(get_class($this), 0, - 5)));
        }

        if ('Zend_Db_Table_Row' === $this->_rowClass) {
            $this->_rowClass = substr(get_class($this), 0, - 5);
        }

        parent::__construct($config);
    }

    /**
     * Fetches rows by searching by criteria(s) over one selected key.
     *
     * If $criterias is an array, it will find all rows that matches one of the criterias.
     * This method always returns a Rowset object, even if only one row was found.
     *
     * @param string $key The key
     * @param mixed                             $criterias The value or an array of values of the searched key(s).
     * @param string|array                      $order  OPTIONAL An SQL ORDER clause.
     * @param int                               $count  OPTIONAL An SQL LIMIT count.
     * @param int                               $offset OPTIONAL An SQL LIMIT offset.
     * @return Zend_Db_Table_Rowset_Abstract Row(s) matching the criteria.
     */
    protected function _findBy($key, $criterias, $order = null, $count = null, $offset = null)
    {
        $whereOrTerms = array();

        foreach ((array) $criterias as $criteria) {
            $whereOrTerms[] = $this->_db->quoteInto($this->_db->quoteIdentifier($key) . ' = ?', $criteria);
        }

        $whereClause = implode(' OR ', $whereOrTerms);

        return $this->fetchAll($whereClause, $order, $count, $offset);
    }

     /**
     * Turn magic function calls into non-magic function calls
     * to the above methods.
     *
     * @param string $method
     * @param array $args OPTIONAL Zend_Db_Table_Select query modifier
     * @return Zend_Db_Table_Row_Abstract|Zend_Db_Table_Rowset_Abstract
     * @throws Zend_Db_Table_Row_Exception If an invalid method is called.
     */
    public function __call($method, array $args)
    {
        $matches = array();

        // Find by Column
        if (preg_match('/^findBy(\w+?)$/', $method, $matches)) {
            if (! in_array($matches[1], $this->_getCols())) {
                $underscoredColumnName = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $matches[1]));
                if (! in_array($underscoredColumnName, $this->_getCols())) {
                    throw new Zend_Db_Table_Row_Exception("Unrecognized column '$matches[1]' " .
                            "or '$underscoredColumnName'");
                }
                $matches[1] = $underscoredColumnName;
            }
            $order = isset($args[1]) ? $args[1] : null;
            $count = isset($args[2]) ? $args[2] : null;
            $offset = isset($args[3]) ? $args[3] : null;

            return $this->_findBy($matches[1], $args[0], $order, $count, $offset);
        }
    }
}